use crate::U256;
use std::fmt;

#[repr(C)]
#[derive(Eq, Clone, Default)]
pub struct Point {
    pub x: U256,
    pub y: U256,
    pub z: U256,
}

impl PartialEq for Point {
    fn eq(&self, other: &Self) -> bool {
        self.x == other.x && self.y == other.y && self.z == other.z
    }
}

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "x: {}, y: {}, z: {}", self.x, self.y, self.z)
    }
}

impl Point {
    #[inline]
    pub fn new(x: &U256, y: &U256, z: &U256) -> Point {
        Point {
            x: x.clone(),
            y: y.clone(),
            z: z.clone(),
        }
    }
    /// affine 2D Point to jacobian
    #[inline]
    pub fn from(x: &U256, y: &U256) -> Point {
        Point {
            x: x.clone(),
            y: y.clone(),
            z: U256::from(1),
        }
    }
    #[inline]
    pub fn mut_ptr(&mut self) -> *mut u64 {
        self.x.mut_ptr()
    }
    #[inline]
    pub fn ptr(&self) -> *const u64 {
        self.x.ptr()
    }
    pub fn x(&self) -> &U256 {
        &self.x
    }
    pub fn y(&self) -> &U256 {
        &self.y
    }
    pub fn z(&self) -> &U256 {
        &self.z
    }
    pub fn select_neg(&self, prime: &U256, sign: u64) -> Point {
        let (y, _) = prime.sub(self.y());
        let y = y.move_cond(self.y(), sign);
        Point {
            x: self.x.clone(),
            y,
            z: self.z.clone(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Point;

    #[test]
    fn test_point_type() {
        use std::mem;
        assert_eq!(mem::size_of::<Point>(), 32 * 3);
    }
}
